<?php
/**
 * Container.php
 * ============================================================================
 * ============================================================================
 * $$Author: 黄迎秋 (18910424482@163.com) $$
 * $$Date: 2021/7/5 8:58 PM $$
 * $$Id$$
 */
 namespace Hphp\Container;
 use Closure;

 class Container implements \ArrayAccess
 {
     // 单例
     protected static $instance;

     /**
      * 容器
      * @var object[]
      */
     protected $bindings = [];

     // 指定的是别名
     // protected $aliases = [];
     // protected $abstractAliases = [];
     /**
      * 共享容器 -》 对容器进行单例的创建和运用
      * @var object[]
      */
     protected $instances = [];

     public function __construct()
     {
         // self::setInstance($this);
     }

     /**
      * 绑定
      * @param  string $abstract 容器标识
      * @param  object $concrete 实例对象/或者对象地址/或者闭包
      */
     public function bind($abstract, $concrete = null, $shared = false)
     {
         $this->bindings[$abstract]['concrete'] = $concrete;
         $this->bindings[$abstract]['shared'] = $shared;
     }

     public function singleton($abstract, $concrete = null, $shared = true)
     {
         $this->bind($abstract, $concrete, $shared);
     }

     /**
      * 解析
      * @param  string $abstract 标识
      * @return return           object/执行的结果
      */
     public function make($abstract, $parameters = [])
     {
         return $this->resolve($abstract, $parameters);
     }

     // return 实例对象/或者对象地址/或者闭包
     public function resolve($abstract, $parameters = [])
     {
//         if (!$this->has($abstract)) {
//             throw new \Exception("解析的对象不存在".$abstract, 500);
//         }

         // 判断是否之前创建过
         if (isset($this->instances[$abstract])) {
             return $this->instances[$abstract];
         }

         // 从容器中获取
         $object = $this->getConrete($abstract);
         //$object = $this->bindings[$abstract]['concrete'];

         // 判断是否为闭包
         if ($object instanceof Closure) {
             // 执行闭包返回结果
             return $object();
         }

         // 判断是否为一个object对象
         if (!is_object($object)) {
             // 创建对象并且return
             $object = new $object(...$parameters);
         }

         // 是否采用单例
         if ($this->has($abstract) && $this->bindings[$abstract]['shared']) {
             $this->instances[$abstract] = $object;
         }

         return $object;
     }

     public function getConrete($abstract)
     {
         if ($this->has($abstract)) {
             $abstract = $this->bindings[$abstract]['concrete'];
         }
         return $abstract;
     }

     public function has($abstract)
     {
         return isset($this->bindings[$abstract]['concrete']) || isset($this->instances[$abstract]);
     }

     public function getBindings()
     {
         return $this->bindings;
     }

     /**
      * 设置指定对象为共享实例[可能这个实例并没有绑定在容器中，因此就额外的设置]
      * @param  string $abstract 标识
      * @param  object $instance 实例对象
      */
     public function instance($abstract, $instance)
     {
         // 1. 从容器中移除
         $this->removeBindings($abstract);
         $this->instances[$abstract] = $instance;
     }
     public function removeBindings($abstract)
     {
         if (!isset($this->bindings[$abstract])) {
             return;
         }
         unset($this->bindings[$abstract]);
     }

     // --------------------------- 单例 ------------------------------
     public static function setInstance($container = null)
     {
         return static::$instance = $container;
     }

     public static function getInstance()
     {
         if (is_null(static::$instance)) {
             static::$instance = new static;
         }
         return static::$instance;
     }

     // ---------------------arrayAccss-------
     public function offsetExists($key)
     {
         var_dump("offsetExists");
     }

     public function offsetGet($key)
     {
         var_dump($key);
         //return $this->make($key);
     }
     public function offsetSet($key, $value)
     {
         var_dump("offsetSet");
     }
     public function offsetUnset($key)
     {
         var_dump("offsetUnset");
     }
 }